# -*- coding: utf-8 -*-

import ast

from ir.ir_helper import Helper
from ir.ir_node import ITypeNode
from ir.ir_statement import *

from .convert_assign import convert_assign, convert_annassign, convert_augassign
from .convert_exp import convert_exp, convert_for_header
from .local_vars import start_local_var, end_local_var


def convert_if(ast_if: ast.If, helper: Helper, this: ITypeNode):
    # As for if, three parts are important.
    #  1. test: which is the condition.
    #  2. body: which is the code executed when condition is True.
    #  3. orelse: which is the code executed when condition is False, optional.
    ir_condition = convert_exp(ast_if.test, helper, this)
    ir_body = convert_body(ast_if.body, helper, this)
    # Check else body.
    if hasattr(ast_if, 'orelse') and len(ast_if.orelse) > 0:
        ir_elbody = convert_body(ast_if.orelse, helper, this)
    else:
        ir_elbody = None
    # Construct the if body.
    return [IOpIf(ir_condition, ir_body, ir_elbody)]


def convert_return(ast_return: ast.Return):
    # Calculate the value result.
    if ast_return.value is not None:
        raise Exception('Unimplemented return detected.')
    return [IOpReturn()]


def convert_for(ast_for: ast.For, helper: Helper, this: ITypeNode):
    # First convert the header of the for loop.
    iter_var, iter_range, iter_init_body, local_map = \
        convert_for_header(ast_for.target, ast_for.iter, helper, this)
    # Start the ticket for loop body.
    # Start convert the body which is the build exp.
    ticket = str(id(ast_for))
    start_local_var(ticket, local_map, helper)
    # Converty the body of for loop.
    loop_body = convert_body(ast_for.body, helper, this)
    end_local_var(ticket, helper)
    # Append the iteration initial body to the beginning of the loop body.
    loop_body.ops = iter_init_body + loop_body.ops
    # Generate the for-loop structure.
    return [IOpFor(iter_var, iter_range, loop_body)]


def convert_body(ast_body: list, helper: Helper, this: ITypeNode) -> IOpBody:
    # Add body ticket.
    body_ticket = str(id(ast_body))
    # Construct the body ir.
    ir_body = []
    start_local_var(body_ticket, {}, helper)
    # Loop and convert all the expression.
    for ast_line in ast_body:
        # Check each line of the body.
        if isinstance(ast_line, ast.Assign):
            ir_body += convert_assign(ast_line, helper, this)
        elif isinstance(ast_line, ast.Return):
            ir_body += convert_return(ast_line)
        elif isinstance(ast_line, ast.If):
            ir_body += convert_if(ast_line, helper, this)
        elif isinstance(ast_line, ast.Expr):
            ir_body += [convert_exp(ast_line.value, helper, this)]
        elif isinstance(ast_line, ast.AnnAssign):
            ir_body += convert_annassign(ast_line, helper, this)
        elif isinstance(ast_line, ast.AugAssign):
            ir_body += convert_augassign(ast_line, helper, this)
        elif isinstance(ast_line, ast.For):
            ir_body += convert_for(ast_line, helper, this)
        elif isinstance(ast_line, ast.Pass):
            ir_body.append(IOpPass())
        elif isinstance(ast_line, ast.Break):
            ir_body.append(IOpBreak())
        elif isinstance(ast_line, ast.Continue):
            ir_body.append(IOpContinue())
        else:
            raise Exception('Unimplemented body line type:' + str(type(ast_line)) + str(ast_line.__dict__))
    # End the local variable state.
    end_local_var(body_ticket, helper)
    # Construct the op body.
    return IOpBody(ir_body)